// This is the main DLL file.

#include "stdafx.h"



//#include <atlbase.h>
#include <oledbdm.h>


#include "DMMetadataAndFactory.h"
#include "DMAttributeSet.h"
using namespace PluginAlgorithms;

using namespace System::Runtime::InteropServices;


/// <summary>
/// returns the flags of the specified attribute
/// </summary>
AttributeFlags      AttributeSet::GetAttributeFlags( System::UInt32   attribute)
{
    USES_ERROR_HANDLING;
	DM_ATTRIBUTE_FLAGS	attFlags = 0;

	THROW_ON_FAILURE( m_wpAttrSet->GetAttributeFlags( (DM_Attribute)attribute, &attFlags) );
	AttributeFlags	flags = (AttributeFlags) attFlags;
	return flags;
}

/// <summary>
/// returns the modeling flags of the specified attribute
/// </summary>
array<MiningModelingFlag>^  AttributeSet::GetModelingFlags( System::UInt32   attribute)
{
	
	USES_ERROR_HANDLING;
	DM_MODELING_FLAG	*rgModelingFlags =NULL;
	ULONG				ulCount = 0;
	
	THROW_ON_FAILURE( m_wpAttrSet->GetModelingFlags( (DM_Attribute)attribute, &rgModelingFlags, &ulCount) );

	array<MiningModelingFlag>^	rgFlags	=	gcnew array<MiningModelingFlag>(ulCount);
	for( ULONG nIndex = 0; nIndex < ulCount; nIndex ++ )
	{
		rgFlags[nIndex] = (MiningModelingFlag)rgModelingFlags[nIndex];
	}
	
	return rgFlags;
}

/// <summary>
/// returns the underlying type of an attribute
/// </summary>
System::Type ^  AttributeSet::GetAttributeType( System::UInt32   attribute)
{
	USES_ERROR_HANDLING;
	DBTYPE	attType	=	DBTYPE_NULL;
	
	
	THROW_ON_FAILURE( m_wpAttrSet->GetAttributeType( (DM_Attribute)attribute, &attType) );
	System::Type ^	type = nullptr;
	switch( attType )
	{
		case DBTYPE_NULL:
			type = nullptr;
			break;
		case DBTYPE_I8:
			type = System::Int64::typeid;
			break;
		case DBTYPE_BOOL:
			type = System::Boolean::typeid;
			break;
		case DBTYPE_WSTR:
			type = System::String::typeid;
			break;
		case DBTYPE_R8:
			type = System::Double::typeid;
			break;
		case DBTYPE_DATE:
			type = System::DateTime::typeid;
			break;
		case DBTYPE_HCHAPTER:
			// bogdanc --  review this!!!
			type = System::Data::DataTable::typeid;
			break;
	}
	return type;
}

/// <summary>
/// returns the number of attributes
/// </summary>
System::UInt32 AttributeSet::GetAttributeCount( )
{
    
	USES_ERROR_HANDLING;
	ULONG ulCount = 0;
	
	
	THROW_ON_FAILURE( m_wpAttrSet->GetAttributeCount( &ulCount ) );
	return ulCount;
}

/// <summary>
/// returns the number of states of the specified attribute
/// </summary>
System::UInt32 AttributeSet::GetAttributeStateCount( System::UInt32 attribute)
{
    USES_ERROR_HANDLING;
	FailIfValidating();

	ULONG ulStates = 0;
	THROW_ON_FAILURE( m_wpAttrSet->GetAttributeStateCount( (DM_Attribute)attribute, &ulStates) );
	return (System::UInt32)ulStates;
}

/// <summary>
/// returns the display name of the specified attribute
/// </summary>
String ^    AttributeSet::GetAttributeDisplayName( System::UInt32 attribute, bool shortName)
{

	USES_ERROR_HANDLING;
	FailIfValidating();


	System::String	^	attrName = nullptr;

	AutoUseContext	apEC;
	HRESULT		hRet	=	S_OK;
	CComPtr<IDMStringHandler>	spStringHandler;
	DMString*	pStrDisplayName = NULL;

	try
	{
		// Get the string handler
		THROW_ON_FAILURE( apEC->GetStringHandler(&spStringHandler) );
		// Create a new Handle
		THROW_ON_FAILURE( spStringHandler->CreateNewStringHandle(&pStrDisplayName) );
	
		// Get the attribute display name
		BOOL	fShortName = shortName ? TRUE : FALSE;

		THROW_ON_FAILURE( m_wpAttrSet->GetAttributeDisplayName( apEC, (DM_Attribute)attribute, fShortName, pStrDisplayName) );
	
		const WCHAR*	lpwszText = NULL;
		UINT ulTextLength = 0;
		THROW_ON_FAILURE( spStringHandler->GetConstStringFromHandle(pStrDisplayName, &lpwszText, &ulTextLength) );

		attrName	=	gcnew System::String( lpwszText, 0, ulTextLength );

		return attrName;

	}
	finally
	{
		if( spStringHandler && pStrDisplayName)
		{
			spStringHandler->DestroyStringHandle( pStrDisplayName );
		}
	}
}


/*
// TODO: bogdanc -- See what to do about this
/// <summary>
/// renders the XML for the display name of the specified attribute
/// </summary>
String __gc*    GetAttributeDisplayNameXML( System::UInt32 attribute );
*/

/// <summary>
/// returns the display namke of the specified attribute
/// </summary>
String ^    AttributeSet::GetFullyQualifiedColumnName( System::UInt32 attribute)
{
	USES_ERROR_HANDLING;
	FailIfValidating();
	AutoUseContext	apEC;

	HRESULT		hRet	=	S_OK;
	CComPtr<IDMStringHandler>	spStringHandler;

	// Create a new Handle
	DMString*	pStrDisplayName = NULL;

	try
	{
		// Get the string handler
		THROW_ON_FAILURE( apEC->GetStringHandler(&spStringHandler) );
		THROW_ON_FAILURE( spStringHandler->CreateNewStringHandle(&pStrDisplayName) );

		THROW_ON_FAILURE( m_wpAttrSet->GetFullyQualifiedColumnName( apEC, (DM_Attribute)attribute, pStrDisplayName));

		const WCHAR*	lpwszText = NULL;
		UINT ulTextLength = 0;
	
		THROW_ON_FAILURE( spStringHandler->GetConstStringFromHandle(pStrDisplayName, &lpwszText, &ulTextLength) );

		System::String	^	colName	=	nullptr;
	
		colName = gcnew System::String( lpwszText, 0, ulTextLength );
		return colName;
	}
	finally
	{
		if( spStringHandler && pStrDisplayName )
		{
			spStringHandler->DestroyStringHandle(pStrDisplayName);
		}
	}
}



/// <summary>
/// returns the attribute value
/// </summary>
Object^     AttributeSet::UntokenizeAttributeValue( System::UInt32 attribute, StateValue^   statevalue)
{
	USES_ERROR_HANDLING;

	FailIfValidating();

	AutoUseContext	apEC;
	VARIANT var;
	::VariantInit(&var);

	CComPtr<IDMVariantPtrHandler> spVarHandler;
	DMVariantPtr	handleVar = NULL;
	

	System::IntPtr wrappedPVar = (System::IntPtr)&var;
	
	Object^ retVal = nullptr;
	// Build a variant that returns the 
	if( statevalue->IsMissing )
	{
		return nullptr;
	}
	else if( statevalue->IsIndex )
	{
		System::Runtime::InteropServices::Marshal::GetNativeVariantForObject( statevalue->Index, wrappedPVar);
	}
	else if (statevalue->IsDouble )
	{
		V_VT(&var)	=	VT_R8;
		V_R8(&var)	=	statevalue->Double;
	}


	

	try
	{
		// Get the string handler
		THROW_ON_FAILURE( apEC->GetVariantHandler(&spVarHandler) );

		// Create a new Handle
		THROW_ON_FAILURE( spVarHandler->CreateNewVariantHandle(&handleVar) );
	
		THROW_ON_FAILURE( spVarHandler->AttachHandleToVariant(handleVar, &var) );
		::VariantClear( &var );	

		THROW_ON_FAILURE( m_wpAttrSet->GetAttributeValue( apEC, (DM_Attribute)attribute, handleVar) );

		::VariantInit( &var );
		THROW_ON_FAILURE( spVarHandler->CopyHandleToVariant( handleVar, &var) );

		retVal = System::Runtime::InteropServices::Marshal::GetObjectForNativeVariant((System::IntPtr)&var);
		

		return retVal;
	}
	finally
	{
		::VariantClear( &var );
		if( spVarHandler && handleVar )
		{
			spVarHandler->DestroyVariantHandle( handleVar );
		}
	}

}

/// <summary>
/// returns the attribute state that describes a specified value
/// </summary>
StateValue ^ AttributeSet::TokenizeAttributeValue( System::UInt32 attribute, Object ^    value)
{
    USES_ERROR_HANDLING;
	FailIfValidating();

	AutoUseContext	apEC;
	HRESULT hRet = S_OK;
	
	VARIANT var;
	::VariantInit(&var);
	
	System::IntPtr wrappedPVar = (System::IntPtr)&var;
	System::Runtime::InteropServices::Marshal::GetNativeVariantForObject( value, wrappedPVar);

	DM_STATE_VALUE dmStateValue;


	CComPtr<IDMVariantPtrHandler> spVarHandler;
	DMVariantPtr	handleVar = NULL;

	try
	{
		// Get the string handler
		THROW_ON_FAILURE( apEC->GetVariantHandler(&spVarHandler) );

		// Create a new Handle
		THROW_ON_FAILURE( spVarHandler->CreateNewVariantHandle(&handleVar) );
		THROW_ON_FAILURE( spVarHandler->AttachHandleToVariant(handleVar, &var) );
	
		THROW_ON_FAILURE( m_wpAttrSet->GetAttributeStateValue( apEC, (DM_Attribute)attribute, handleVar, &dmStateValue) );
	
		StateValue ^ val = gcnew StateValue();
		val->Set( dmStateValue );

	    return val;
	}
	finally
	{
		::VariantClear(&var);
		if( spVarHandler && handleVar )
		{
			spVarHandler->DestroyVariantHandle( handleVar );
		}
	}
}


/*
// TODO: bogdanc -- See what to do about this --  ambiguous behavior of the statevaluie parameter
// and can be replaced by GetAttributeStateValue
/// <summary>
/// returns the attribute value
/// </summary>
void    GetInterpretedAttributeValue( System::UInt32 attribute, Object __gc*& statevalue);
*/

void    AttributeSet::GetDiscretizedStateRange( 
    System::UInt32  attribute,
    System::UInt32  stateIndex,
    bool            actual,
    System::Double%  lower,
    System::Double%  upper)
{
	USES_ERROR_HANDLING;
	FailIfValidating();

	AutoUseContext apEC;
	
	double	dblLower = 0.0, dblUpper = 0.0;
	THROW_ON_FAILURE( m_wpAttrSet->GetDiscretizedStateRange(apEC, (DM_Attribute)attribute, (DM_State)stateIndex, actual, &dblLower, &dblUpper) );
	lower	=	(System::Double)dblLower;
	upper	=	(System::Double)dblUpper;
}


void    AttributeSet::GetDiscretizedStateRange( 
    System::UInt32  attribute,
    System::UInt32  stateIndex,
    bool            actual,
    System::DateTime%  lower,
    System::DateTime%  upper)
{
	USES_ERROR_HANDLING;
	FailIfValidating();

	AutoUseContext apEC;
	double	dblLower = 0.0, dblUpper = 0.0;

	THROW_ON_FAILURE( m_wpAttrSet->GetDiscretizedStateRange(apEC, (DM_Attribute)attribute, (DM_State)stateIndex, actual, &dblLower, &dblUpper) );
	lower	=	System::DateTime::MinValue;
	upper	=	System::DateTime::MinValue;

	lower.AddTicks( *((__int64*)&dblLower) );
	upper.AddTicks( *((__int64*)&dblUpper) );
}


bool    AttributeSet::IsFunctionallyDependant( 
    System::UInt32  attr1,
    System::UInt32  attr2)
{
	USES_ERROR_HANDLING;
    
	THROW_ON_FAILURE( m_wpAttrSet->IsFunctionallyDependant( (DM_Attribute)attr1, (DM_Attribute)attr2) );
	if( CURRENT_STATUS == S_OK )
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool    AttributeSet::IsCaseAttribute( 
    System::UInt32  attribute)
{
	USES_ERROR_HANDLING;
    THROW_ON_FAILURE( m_wpAttrSet->IsCaseAttribute( (DM_Attribute)attribute ) );
	if( S_OK == CURRENT_STATUS )
	{
		return true;
	}
 	else
	{
		return false;
	}
}

System::String ^ AttributeSet::GetCaseIdName(long        caseId)
{
	USES_ERROR_HANDLING;
	FailIfValidating();

	AutoUseContext apEC;
	CComPtr<IDMStringHandler>	spStringHandler;
	DMString*	pStrDisplayName = NULL;

	
	try
	{
		// Get the string handler
		THROW_ON_FAILURE( apEC->GetStringHandler(&spStringHandler) );
		

		// Create a new Handle
		THROW_ON_FAILURE( spStringHandler->CreateNewStringHandle(&pStrDisplayName) );
		

		THROW_ON_FAILURE( m_wpAttrSet->GetCaseIDName( apEC, (DM_CaseID)caseId, pStrDisplayName) );
		
		const WCHAR*	lpwszText = NULL;
		UINT ulTextLength = 0;
		THROW_ON_FAILURE( spStringHandler->GetConstStringFromHandle(pStrDisplayName, &lpwszText, &ulTextLength) );

		System::String	^	caseIdName	=	nullptr;
		caseIdName = gcnew System::String( lpwszText, 0, ulTextLength );

		return caseIdName;
	}
	finally
	{
		if( spStringHandler && pStrDisplayName )
		{
			spStringHandler->DestroyStringHandle( pStrDisplayName );
		}
	}
}


System::UInt32		AttributeSet::GetClassifiedAttribute(
						System::UInt32		attribute)
{


	AutoUseContext apEC;
	HRESULT		hRet	=	S_OK;

	DM_Attribute	dmAttr = DM_UNSPECIFIED;
	
	USES_ERROR_HANDLING;
	THROW_ON_FAILURE(m_wpAttrSet->GetClassifiedAttribute( (DM_Attribute)attribute, &dmAttr));

	return (System::UInt32)dmAttr;

}


AttributeGroup ^ AttributeSet::GetAttributeGroup( 
						System::UInt32 attribute )
{
	USES_ERROR_HANDLING;
	FailIfValidating();

	AutoUseContext apEC;
	
	DM_Attribute	dmAttr = DM_UNSPECIFIED;
	CComPtr<IDMAttributeGroup>	spAttrGroup;
	
	THROW_ON_FAILURE(m_wpAttrSet->GetAttributeGroup( apEC, (DM_Attribute)attribute, &spAttrGroup));

	System::Collections::ArrayList ^	rgArrAtts	=	gcnew System::Collections::ArrayList();

	THROW_ON_FAILURE( spAttrGroup->Reset() );
	while ((CURRENT_STATUS = spAttrGroup->Next(&dmAttr)) != S_FALSE)
	{
		THROW_ON_FAILURE(CURRENT_STATUS);

		if( dmAttr == DM_UNSPECIFIED )
			continue;

		
		rgArrAtts->Add( ((System::UInt32)dmAttr));
	}


	AttributeGroup ^	ret = gcnew AttributeGroup( rgArrAtts );
	return ret;
}




/////////////////////////////////////////////////////////
// AttributeStatistics
//
AttributeStatistics::~AttributeStatistics()
{
	m_pStatesColl = nullptr;
	m_mstrNodeID = nullptr;

}

StateStatisticsCollection ^	AttributeStatistics::StateStatistics::get()
{
	if( m_pStatesColl == nullptr )
	{
		m_pStatesColl = gcnew StateStatisticsCollection();
		m_pStatesColl->SetAttributeStatistics( this );
	}
	return m_pStatesColl;
}


System::String ^  AttributeStatistics::NodeId::get()
{
	USES_ERROR_HANDLING;
	if( m_mstrNodeID == nullptr )
	{
		if( m_attStat.strNodeID != NULL )
		{
			
			AutoUseContext	apEC;
			HRESULT		hRet	=	S_OK;
			CComPtr<IDMStringHandler>	spStringHandler;

			// Get the string handler
			THROW_ON_FAILURE( apEC->GetStringHandler(&spStringHandler) );
			
			const wchar_t*	szNodeID = NULL;
			UINT			ulNodeIdLength = 0;
			THROW_ON_FAILURE( spStringHandler->GetConstStringFromHandle( m_attStat.strNodeID, &szNodeID, &ulNodeIdLength) );

			m_mstrNodeID	=	gcnew System::String(szNodeID, 0, ulNodeIdLength);


		}
	}

	return m_mstrNodeID;
}

void	AttributeStatistics::NodeId::set(System::String ^  nodeId)
{
	m_mstrNodeID	=	nodeId;
}





/////////////////////////////////////////////////////////
// MarginalStat
//

System::UInt32      MarginalStats::GetTotalCasesCount()
{
	USES_ERROR_HANDLING;
	AutoUseContext	apEC;
	ULONG ulCount = 0;
	THROW_ON_FAILURE( m_wpMarginalStat->GetTotalCasesCount(apEC, &ulCount)  );
	return (System::UInt32)ulCount;
}

System::UInt32      MarginalStats::GetCasesCount(System::UInt32 attribute, System::UInt32 stateIndex)
{
	USES_ERROR_HANDLING;
	AutoUseContext	apEC;
	ULONG ulCount = 0;
	
	THROW_ON_FAILURE( m_wpMarginalStat->GetCasesCount(apEC, (DM_Attribute)attribute, (DM_State)stateIndex, &ulCount) );
	return (System::UInt32)ulCount;
}


System::Double		MarginalStats::GetCaseDensity()
{
	USES_ERROR_HANDLING;
	AutoUseContext	apEC;
	DOUBLE	dblDensity = 0.0;
	
	THROW_ON_FAILURE( m_wpMarginalStat->GetCaseDensity(apEC, &dblDensity) );
	return (System::Double)dblDensity;
}



AttributeStatistics	^	MarginalStats::GetAttributeStats(System::UInt32 attribute)
{
	USES_ERROR_HANDLING;
	CComPtr<IDMMemoryAllocator> spAlloc;
	AutoUseContext	apEC;
	
	AttributeStatistics ^	stat = gcnew AttributeStatistics();
	pin_ptr<DM_ATTRIBUTE_STAT>		pAttStat = &stat->m_attStat;
	memset( pAttStat, 0, sizeof(DM_ATTRIBUTE_STAT) );

	DM_STATE_STAT*			pStateStats = NULL;
	

	pAttStat->Attribute	=	(DM_Attribute)attribute;

	DM_TRY
	{
		THROW_ON_FAILURE( apEC->GetVectorAlloc(&spAlloc) );
	
		// Get the number of states
		THROW_ON_FAILURE( m_wpAttrSet->GetAttributeStateCount( (DM_Attribute)attribute, &pAttStat->ulStates) );
	
		// Get the number of states
		pin_ptr<DM_ATTRIBUTE_FLAGS> pAttFlags = &stat->m_dmaf;
		THROW_ON_FAILURE( m_wpAttrSet->GetAttributeFlags( (DM_Attribute)attribute, pAttFlags) );


		// Allocate vector for state statistics
		THROW_ON_FAILURE( spAlloc->Alloc( pAttStat->ulStates*sizeof(DM_STATE_STAT), (BYTE**)&pStateStats) );
		

		pAttStat->rgStateStats	=	pStateStats;
		THROW_ON_FAILURE( m_wpMarginalStat->GetAttributeStats( apEC, (DM_Attribute)attribute, pAttStat) );
		pAttStat->rgStateStats	= NULL;


		for( ULONG nIndex = 0; nIndex < pAttStat->ulStates; nIndex ++ )
		{
			DM_STATE_STAT&	pSState = pStateStats[nIndex];
			StateStatistics^ stateStat = gcnew StateStatistics();

			pin_ptr<DM_STATE_STAT> pDest = &stateStat->m_StateStat;
			memcpy( pDest, &pSState, sizeof(DM_STATE_STAT) );

			stat->StateStatistics->Add( stateStat );
		}


		
		return stat;
	}
	DM_FINALLY
	{
		if( spAlloc && pStateStats	 != NULL )
		{
			spAlloc->Free((BYTE*)pStateStats	);
		}
	}
	return nullptr;
}


// STateValue methods
void StateValue::UpdateStateStats()
{
	if( stateStats != nullptr )
	{
		pin_ptr<DM_STATE_VALUE> ppDestVal = &stateStats->m_StateStat.Value;
		pin_ptr<DM_STATE_VALUE> ppVal = &m_val;
		*ppDestVal = *ppVal;
	}
}


void    StateValue::Set(StateStatistics^  stats) 
{
	pin_ptr<DM_STATE_VALUE> ppVal = &m_val;
	pin_ptr<DM_STATE_VALUE> ppSrcVal = &stats->m_StateStat.Value;
	*ppVal = *ppSrcVal;

	stateStats = stats;
}
